package com.ecloud.gateway.config;

import java.util.List;
import java.util.Properties;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.annotation.Configuration;
import org.yaml.snakeyaml.Yaml;

import com.alibaba.cloud.nacos.NacosConfigProperties;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.AbstractListener;
import com.alibaba.nacos.api.exception.NacosException;
import com.ecloud.gateway.model.GatewayRoute;

import reactor.core.publisher.Mono;

/**
 * 动态路由配置
 *
 * @author LiuGangQiang Create in 2021/07/05
 */
@Configuration
public class DynamicRouteConfiguration {
    private static final Logger LOGGER = LoggerFactory.getLogger(DynamicRouteConfiguration.class);
    private final RouteDefinitionWriter rdw;
    private final NacosConfigProperties ncp;
    @Resource
    private RouteDefinitionLocator rdl;
    /**
     * NACOS组
     *
     * @author LiuGangQiang Create in 2021/07/06
     */
    @Value("${spring.cloud.nacos.config.group:DEFAULT_GROUP}")
    private String group;
    /**
     * 配置文件名
     *
     * @author LiuGangQiang Create in 2021/07/06
     */
    @Value("${spring.cloud.nacos.config.dynamic.config}")
    private String dynamic;
    /**
     * 读取超时时间
     *
     * @author LiuGangQiang Create in 2021/07/06
     */
    @Value("${spring.cloud.nacos.config.dynamic.timeout:30000}")
    private Long timeout;

    /**
     * 构造器
     *
     * @author LiuGangQiang Create in 2021/07/05
     * @param rdw 路由操作对象
     * @param ncp NACOS配置对象
     */
    public DynamicRouteConfiguration(RouteDefinitionWriter rdw, NacosConfigProperties ncp) {
        this.rdw = rdw;
        this.ncp = ncp;
    }

    /**
     * 初始化路由
     *
     * @author LiuGangQiang Create in 2021/07/05
     */
    @PostConstruct
    public void initRoute() {
        try {
            Properties properties = new Properties();
            properties.put(PropertyKeyConst.SERVER_ADDR, ncp.getServerAddr());
            properties.put(PropertyKeyConst.USERNAME, ncp.getUsername());
            properties.put(PropertyKeyConst.PASSWORD, ncp.getPassword());
            properties.put(PropertyKeyConst.NAMESPACE, ncp.getNamespace());
            ConfigService cs = NacosFactory.createConfigService(properties);
            /* 初始化网关路由 */
            String content = cs.getConfig(dynamic, group, timeout);
            LOGGER.info("initialization of gateway routing started");
            handleRoute(content);
            LOGGER.info("end initialization gateway route");
            /* 添加监听器,实现动态路由 */
            cs.addListener(dynamic, group, new AbstractListener() {
                @Override
                public void receiveConfigInfo(String configInfo) {
                    LOGGER.info("start updating gateway route");
                    handleRoute(configInfo);
                    LOGGER.info("end update gateway route");
                }
            });
        } catch (NacosException e) {
            LOGGER.error("load gateway route error {}", e.getMessage());
        }
    }

    /**
     * 修改路由 解决删除路由不生效问题
     *
     * @author LiuGangQiang Create in 2021/07/05
     * @param content 配置文件内容
     */
    public void handleRoute(String content) {
        /* 获取缓存的路由并全部清空 */
        List<RouteDefinition> rds = rdl.getRouteDefinitions().buffer().blockFirst();
        if (rds != null && rds.size() > 0) {
            rds.stream().forEach(route -> {
                rdw.delete(Mono.just(route.getId())).subscribe();
            });
        }
        /* 获取新路由并循环添加到缓存 */
        Yaml yaml = new Yaml();
        GatewayRoute routes = yaml.loadAs(content, GatewayRoute.class);
        routes.getRoutes().forEach(route -> {
            LOGGER.info("load gateway route [{}]", route.getId());
            rdw.save(Mono.just(route)).subscribe();
        });
    }
}